package com.ahdms.es.gm.asn1.cipher;

import com.ahdms.es.util.SM2Utils;
import org.bouncycastle.asn1.*;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.BigIntegers;

import java.math.BigInteger;

public class SM2PublicKey extends ASN1Object {

    DERBitString publicKey;
    byte type = 0x04;
    ASN1Integer XCoordinate;
    ASN1Integer YCoordinate;

    public static SM2PublicKey getInstance(Object o) {
        if (o instanceof SM2PublicKey) {
            return (SM2PublicKey) o;
        } else if (o instanceof ASN1Sequence) {
            ASN1Sequence sequence = (ASN1Sequence) o;
            return new SM2PublicKey(DERBitString.getInstance(sequence.getObjectAt(0)));
        }

        throw new IllegalArgumentException("unknown object in factory: " + o);
    }

    public SM2PublicKey(byte[] x, byte[] y) {

        byte[] pubKeyBytes = new byte[65];
        pubKeyBytes[0] = (byte) 0x04;

        int xLength = x.length;
        int yLength = y.length;
        BigInteger X;
        BigInteger Y;
        if (xLength == 32 && yLength == 32) {
            X = new BigInteger(1, x);
            Y = new BigInteger(1, y);

            System.arraycopy(x, 0, pubKeyBytes, 1, x.length);
            System.arraycopy(y, 0, pubKeyBytes, 33, y.length);

        } else if (xLength == 64 && yLength == 64) {
            byte[] subX = new byte[32];
            System.arraycopy(x, 32, subX, 0, 32);
            byte[] subY = new byte[32];
            System.arraycopy(y, 32, subY, 0, 32);

            X = new BigInteger(1, subX);
            Y = new BigInteger(1, subY);

            System.arraycopy(subX, 0, pubKeyBytes, 1, subX.length);
            System.arraycopy(subY, 0, pubKeyBytes, 33, subY.length);

        } else {
            throw new IllegalArgumentException("key length not equal 64 or 65.");
        }
        //校验是否是sm2曲线的点
        ECPoint Q = SM2Utils.validSM2ECCurvePoint(X, Y);
        if (null == Q) {
            throw new IllegalArgumentException("not sm2 ECPoint.");
        }
        XCoordinate = new ASN1Integer(X);
        YCoordinate = new ASN1Integer(Y);

        publicKey = new DERBitString(pubKeyBytes);
    }

    public SM2PublicKey(DERBitString pubKey) {
        byte[] keyBytes = pubKey.getBytes();

        BigInteger X;
        BigInteger Y;
        byte[] pubKeyBytes = new byte[65];
        pubKeyBytes[0] = (byte) 0x04;

        if (64 == keyBytes.length) {
            X = BigIntegers.fromUnsignedByteArray(keyBytes, 0, 32);
            Y = BigIntegers.fromUnsignedByteArray(keyBytes, 32, 32);

            System.arraycopy(keyBytes, 0, pubKeyBytes, 1, keyBytes.length);
        } else if (65 == keyBytes.length) {
            if (type != keyBytes[0]) {
                throw new IllegalArgumentException("sm2 public key's type must be 0x04.");
            }

            X = BigIntegers.fromUnsignedByteArray(keyBytes, 1, 32);
            Y = BigIntegers.fromUnsignedByteArray(keyBytes, 33, 32);

            System.arraycopy(keyBytes, 0, pubKeyBytes, 0, keyBytes.length);
        } else {
            throw new IllegalArgumentException("key length not equal 64 or 65.");
        }

        //校验是否是sm2曲线的点
        ECPoint Q = SM2Utils.validSM2ECCurvePoint(X, Y);
        if (null == Q) {
            throw new IllegalArgumentException("not sm2 ECPoint.");
        }

        XCoordinate = new ASN1Integer(X);
        YCoordinate = new ASN1Integer(Y);

        publicKey = new DERBitString(pubKeyBytes);
    }

    public SM2PublicKey(ECPoint Q) {
        BigInteger X = Q.normalize().getXCoord().toBigInteger();
        BigInteger Y = Q.normalize().getYCoord().toBigInteger();

        XCoordinate = new ASN1Integer(X);
        YCoordinate = new ASN1Integer(Y);

        byte[] pubKeyBytes = Q.getEncoded(false);

        publicKey = new DERBitString(pubKeyBytes);
    }

    public DERBitString getPublicKey() {
        return publicKey;
    }

    public ASN1Integer getXCoordinate() {
        return XCoordinate;
    }

    public ASN1Integer getYCoordinate() {
        return YCoordinate;
    }

    public byte getType() {
        return type;
    }

    @Override
    public ASN1Primitive toASN1Primitive() {
        ASN1EncodableVector v = new ASN1EncodableVector();
        v.add(publicKey);
        return new DERSequence(v);
    }

    @Override
    public byte[] getEncoded() {
        return publicKey.getBytes();
    }

}
